/** This will now become the official to-do-list posting board: **/

/* NOTE: Please prefix all to-do's with 'TODO: ', for example 'TODO: Fix foo to use bar instead of baz'
 *
 * TODO: SMDLoader: Step 2, implement loading and displaying textures
 * TODO: UModel: Design new UModel class that will fulfill current design goals and can be easily expanded upon (Current status: WIP, Awaiting pear-review)
 * TODO: UModelViewer: Implement ArcBall rotation
 * TODO: Fix all *warnings* (-Wall -Werror)
 * FIXME: Change boost::mutex::scoped_locks in CPPSDLGL.cpp back to scoped_try_locks (BUG in Boost 1.35.0)
 */





/********************************************************************/





// CPPSDLGL.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include "UDockable.h"
#include "UWindowManager.h"
#include "URonddraaiendeTriangle.h"
#include "UTexturedWindow.h"
#include "UGLFont.h"
#include <time.h>
#include "UStringUtils.h"
#include "UModel.h"
#include "UConsoleProgramWindow.h"
#include "CN2IC10/UCN2IC10Windows.h"
#include "UTestControlWindow.h"
#include "UWindowThemer.h"
#include "Game/UGame.h"
#include "UModelViewer.h"
#include "UOpenFileDialog.h"
#include "USMDLoader.h"
#include "UOBJLoader.h"
#include "UGlobalGLFunctions.h"
#include "Game/UViewPortWindow.h"
#include <boost/thread/mutex.hpp>
#include <iostream>
#include <sstream>
using namespace std;
#undef main

#define DEFAULT_RESOLUTION	1
#define BOOST_CMDLINE_PARSER

static volatile uint32 FPSCounter;
static volatile float fps;

static volatile bool canSetWindowCaption = false;
boost::try_mutex windowCaptionMutex;

static Uint32 timerTickCallback(Uint32 interval, void* param) {
//	boost::try_mutex::scoped_try_lock scoped_try_lock(windowCaptionMutex);
	boost::mutex::scoped_lock scoped_try_lock(windowCaptionMutex);
	//if(scoped_try_lock.locked()) {
		if(!canSetWindowCaption) {
			fps = ((((float)FPSCounter) / ((float)interval))*1000.0); //FIXME: interval is always 1000, not the actual ammount of time that has passed
			canSetWindowCaption = true;
		}
//	} else { cerr << "[TimerThread] Unable to aquire lock!\n"; } // TODO: See if cerr is thread safe
	FPSCounter=0;
	return(interval);
}

// void SetVSync(bool sync) {
// 	typedef bool (APIENTRY *PFNWGLSWAPINTERVALFARPROC)(int);
//
// 	PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = 0;
//
// 	wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)SDL_GL_GetProcAddress("wglSwapIntervalEXT");
// 	if(wglSwapIntervalEXT)
// 		wglSwapIntervalEXT(sync);
// }

#ifdef BOOST_CMDLINE_PARSER
struct options {
	bool fullscreen;
};

#include <boost/program_options.hpp>
namespace coolparser {
	using namespace boost::program_options;
	bool parse_options(options& opt, int ac, char* av[])
	{
		options_description desc("General Options");
		desc.add_options()
			("help", "produce help")
			("fullscreen,f", bool_switch(&opt.fullscreen)->default_value(false), "fullscreen mode")
			;

		variables_map vm;
		try {
			store(parse_command_line(ac, av, desc), vm);
			notify(vm);
		} catch (unknown_option& e) {
			cout << "ERROR: " << e.what() << "\n";
			cout << desc << "\n";
			return false;
		}

		if (vm.count("help")) {
			cout << desc << "\n";
			return false;
		}
		return true;
	}
}
using coolparser::parse_options;
#endif

int main(int argc, char* argv[])
{
#ifdef BOOST_CMDLINE_PARSER
	options opt;
	if (!parse_options(opt, argc, argv))
		return 1;
#endif
	SDL_Surface *screen;
	SDL_Event event;
	int quit = 0;

	srand( (unsigned)time( NULL ) );

	const SDL_VideoInfo* info = NULL;
	int flags	= 0;

/* ----- SDL init --------------- */
	if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) {
		cerr << STRFORMAT("Video initialization failed: %s\n", SDL_GetError());
		exit(-1);
	}
	cerr << "SDL is initialised, you may now use OpenGL functions (AND NOT BEFORE THIS LINE)\n";
	atexit(SDL_Quit);

/* ----- OpenGL attribute setting via SDL --------------- */
	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	//NOTE: This setting appears to be ignored by the current ATI driver (catalyst 8.7)
	//      However setting the vsync slider in the control panel to 'On, unless application specifies'
	//      seems like a work around, however what actually happens is that eventhough coolgame
	//      no longer uses 100% cpu, that role is now taken over by the X process (ati-binary-blob-driver)
	//      so cpu usage is still effectively 100%, and in fact GUI responsiveness has actuallly decreased :(
	//      So for now I'll have accept 200+fps/100% cpu usage...
	if(SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1) == 0) {
		cerr << "Successfully requested swap control to be turned on.\n";
	}
	else cerr << STRFORMAT("SDL_GL_SetAttribute() failed; SDL_GetError() returns %i\n", SDL_GetError());


	flags = SDL_OPENGL;// | SDL_NOFRAME;
#ifdef BOOST_CMDLINE_PARSER
	if (opt.fullscreen) {
		flags |= SDL_FULLSCREEN;
		ScreenWidth = 1024;
		ScreenHeight = 768;
	}
#endif

/* ----- Setting up the screen surface --------------- */
	if((screen = SDL_SetVideoMode(ScreenWidth, ScreenHeight,  SDL_GetVideoInfo()->vfmt->BitsPerPixel, flags | 0x0c00)) == 0) {
		cerr << STRFORMAT("Video mode set failed: %s\n", SDL_GetError());
        	exit(-1);
	}
	SDL_ShowCursor(0);

/* ----- OpenGL init --------------- */
	string extensions = string((char *)glGetString(GL_EXTENSIONS));
	if (extensions.find("GL_ARB_vertex_buffer_object") != string::npos)
	{
		cerr << STRFORMAT("Good, it appears ARB extensions are present :) (*FAST*)\n");
		glBindBufferARB = (PFNGLBINDBUFFERARBPROC)SDL_GL_GetProcAddress("glBindBufferARB");
		glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)SDL_GL_GetProcAddress("glGenBuffersARB");
		glBufferDataARB = (PFNGLBUFFERDATAARBPROC)SDL_GL_GetProcAddress("glBufferDataARB");
	}
	else {
		cerr << STRFORMAT("Uhoh, it appears ARB extensions aren't present :( (*SLOW*)\n");
	}

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, ScreenWidth, -ScreenHeight, 0, -1, 1);
	glScalef(1, -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glClearColor(0.8f, 0.8f, 0.8f, 0.0f);
	glDisable(GL_DEPTH);  /* This is a 2d program, no need for depth test */

	wt = new TWindowThemer();
	WindowManager = new TWindowManager(ScreenWidth, ScreenHeight, wt);
 	TSMDLoader* SMDLoader = new TSMDLoader();
	TOBJLoader* OBJLoader = new TOBJLoader();
	Game = new TGame();

	cerr << STRFORMAT("Before Creating Windows\n");
// 	WindowManager->NewWindow(10	, 110, 100, 200, new TCN2IC10MainWindow(),	wt, "CN2IC10 - Network Client" );
/*
#ifdef WIN32
	WindowManager->NewWindow(110, 110, 200, 200, new TConsoleProgramWindow("cmd"), wt, "Console - cmd" );
#else
	WindowManager->NewWindow(110, 110, 200, 200, new TConsoleProgramWindow("bash"), wt, "Console - bash" );
#endif
*/

//	WindowManager->NewWindow(ScreenWidth-200, 20, ScreenWidth-10, 200, new TTestControlWindow(), wt, "Test Control" );

//	WindowManager->NewWindow(200, 50, ScreenWidth-200, 600, new TWorldCreatorWindow(), wt, "World Creator" );
//	WindowManager->NewWindow(ScreenWidth/2-320, ScreenHeight/2-240, ScreenWidth/2+320, ScreenHeight/2+240, new TModelViewer(OBJLoader->loadModelFromFile("Data/Models/Units/M1A1.ini")), wt, "Model viewer");

//	WindowManager->NewWindow(50, 50, 550, 550, new TViewPortWindow(Game->GUI), wt, "ViewPortWindow");
//	WindowManager->NewWindow(100, 100, 500, 400, new TOpenFileDialog(), wt, "Open File..");
	WindowManager->NewWindow((ScreenWidth>>1) - 320, (ScreenHeight>>1) - 240,
	                         (ScreenWidth>>1) + 320, (ScreenHeight>>1) + 240,
	                         new TModelViewer( SMDLoader->loadModelFromFile( "Data/Models/Units/smd/gordon/gordon_ref.smd" ) ),
	                         wt,
	                         "Modelviewer");


	cerr << STRFORMAT("After Creating Windows\n");
	uint32 TimeCheckCount= 0;

	SDL_TimerID FPSTimer = SDL_AddTimer(1000, timerTickCallback, NULL);
	while (!quit) {
		while (SDL_PollEvent(&event)) {

			WindowManager->EventHandle(event);
			switch (event.type)
			{
				case SDL_QUIT:
					quit = 1;
					break;
				case SDL_KEYDOWN:
				{
					switch( event.key.keysym.sym ){
					case SDLK_ESCAPE:
						quit = 1;
						break;
					case SDLK_F1:
						SDL_WM_ToggleFullScreen(screen);
						break;
					}
				}
			}
		}
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

		glViewport(0,0, ScreenWidth, ScreenHeight);
		//////////////Set up opengl for drawing 2d/////////////////
		glDisable(GL_DEPTH_TEST);
		glShadeModel(GL_SMOOTH);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, ScreenWidth, ScreenHeight, 0, -1, 1);
		glMatrixMode(GL_MODELVIEW);
		//////////////Set up opengl for drawing 2d/////////////////

		glLoadIdentity();
		glColor3f(1,1,1);

		WindowManager->Draw();
		glColor3ub(0,0,0);

		glFlush();
		SDL_GL_SwapBuffers();
		{
//			boost::try_mutex::scoped_try_lock scoped_try_lock(windowCaptionMutex);
			boost::mutex::scoped_lock scoped_try_lock(windowCaptionMutex);
//			if(scoped_try_lock.locked()) {
				++FPSCounter;
				if(canSetWindowCaption) {
					SDL_WM_SetCaption( STRFORMAT("FPS: %4.2f", fps).c_str(), NULL);
					canSetWindowCaption=false;
				}
//			} else { cerr << "[Main loop] Unable to aquire lock!\n"; }
		}
//		#ifndef WIN32
//			usleep(1000000/60); //Possibly fixes X11 crash/hang bug on linux? wtfux?
//		#endif
	}

/* ----- Quitting --------------- */
	cerr << STRFORMAT("Cleaning SDL\n");
	SDL_RemoveTimer(FPSTimer);
	cerr << STRFORMAT("Shutdown -h now\n");
	return 0;
}
